class TypedEvent implements IEvent {
    // Private member vars
    private listeners :any[];

    constructor() {
        this.listeners = [];
    }

    /// <summary>Registers a new listener for the event.</summary>
    /// <param name="listener">The callback function to register.</param>
    public on(listener :() => void) :void {
        if (listener === undefined || listener === null)
            throw ("listener parameter may not be undefined or null.");

        this.listeners.push(listener);
    }

    public once(listener :() => void) :void {
        if (listener === undefined || listener === null)
            throw ("listener parameter may not be undefined or null.");

        var _event = this;
        var wrappedListener = function () {
            _event.off(wrappedListener);
            listener.apply(this, arguments);
        };

        this.listeners.push(wrappedListener);
    }

    /// <summary>Unregisters a listener from the event.</summary>
    /// <param name="listener">
    ///     The callback function that was registered. Optional.
    ///     If undefined, then all listeners will be removed.
    /// </param>
    public off(listener? :() => void): void {
        if (typeof listener === 'function') {
            var listenerIndex :number = 0;
            for (; listenerIndex < this.listeners.length; ++listenerIndex) {
                if (this.listeners[listenerIndex] === listener) {
                    this.listeners.splice(listenerIndex, 1);
                    break;
                }
            }
        } else {
            this.listeners = [];
        }
    }

    /// <summary>Invokes all of the listeners for this event.</summary>
    /// <param name="args">Optional set of arguments to pass to listners.</param>
    public fire(...a :any[]) :void {
        var context = {};
        var callbackArguments = (a === undefined || a === null) ? [] : a;
        var listeners = this.listeners.slice(0);

        var listenerIndex :number = 0;
        for(; listenerIndex < listeners.length; ++listenerIndex) {
            listeners[listenerIndex].apply(context, a || []);
        }
    }
}